#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "rpc.hpp"

#define LOC_REQUEST		1
#define LOC_SUCCESS		2
#define LOC_FAILURE		3
#define REGISTER		4
#define EXECUTE			5
#define EXECUTE_SUCCESS 6
#define EXECUTE_FAILURE 7
#define TERMINATE		8

using namespace std;

char* myMarshall(int type, int *buffersize, char *name, int * argTypes, void** args, in_addr_t ip, int port, int reasoncode) {
	int ip_len, port_len, name_len, type_len;
	int offset, index;
	char *buffer = NULL;
	switch(type){
		case 1: // LOC_REQUEST
			name_len = 20;
			type_len = 0;
			while(argTypes[type_len] != 0){
				type_len ++;
			}
			*buffersize = name_len + (type_len+1)*sizeof(int);
			buffer = new char[*buffersize];
			memset(&buffer[0], 0, *buffersize);
			
			offset = 0;
			// function name
			strncpy(&buffer[offset], name, name_len);
			offset += name_len;

			//argtypes
			index = 0;
			while(argTypes[index] != 0){
				memcpy(&buffer[offset], &argTypes[index], sizeof(int));
				offset += sizeof(int);
				index++;
			}
			memcpy(&buffer[offset], &argTypes[index], sizeof(int));
			return buffer;
		break;
		
		case 2: // LOC_SUCCESS
			ip_len = sizeof(in_addr_t);
			port_len = sizeof(int);
			*buffersize = ip_len + port_len;
			buffer = new char[*buffersize];
			memset(&buffer[0], 0, *buffersize);
			offset = 0;
			//ip
			memcpy(&buffer[offset], &ip, ip_len);
			offset += ip_len;
			// port
			memcpy(&buffer[offset], &port, port_len);
			offset += port_len;
			return buffer;
		break;
		
		case 3:
		case 7: // LOC_FAILURE
			*buffersize = sizeof(int);
			buffer = new char[*buffersize];
			memset(&buffer[0], 0, *buffersize);
			offset = 0;
			//reasoncode
			memcpy(&buffer[offset], &reasoncode, sizeof(int));
			offset += sizeof(int);
			return buffer;
		break;

		case 4:
			//REGISTER" IP address, port, name, argTypes
			ip_len = sizeof(in_addr_t);
			port_len = 4;
			name_len = 20;
			type_len = 0;
			while(argTypes[type_len] != 0){
				type_len ++;
			}
			*buffersize = ip_len + port_len + name_len + (type_len+1) * sizeof(int);
			buffer = new char[*buffersize];
			memset(&buffer[0], 0, *buffersize);

			// buffer offset
			offset = 0;
			
			//ip	
			memcpy(&buffer[offset], &ip, sizeof(in_addr_t));
			offset += sizeof(in_addr_t);
			
			// port
			memcpy(&buffer[offset], &port, sizeof(int));
			offset += sizeof(int);

			// function name
			strcpy(&buffer[offset], name);
			offset += name_len;

			//argtypes
			index = 0;
			while(argTypes[index] != 0){
				memcpy(&buffer[offset], &argTypes[index], sizeof(int));
				offset += sizeof(int);
				index++;
			}
			memcpy(&buffer[offset], &argTypes[index], sizeof(int));
			return buffer;
		case 5:
		case 6:
			//REGISTER" IP address, port, name, argTypes
		cout << "in marshall" << endl;
			name_len = 20;
			type_len = 0;
			int arg_len = 0;
			int arg_num_len = sizeof(int);
			while(argTypes[type_len] != 0){
				type_len ++;
			}
			
			int arg_num = type_len + 1;
			for(int i = 0; i < type_len; i++) {
				arg_len += sizeof(args[i]);
			}
			// args content size
			int arg_size = 0;
			for(int i = 0; i < type_len; i++) {
				int arraysize = argTypes[i] & 0x0000FFFF;
				int type = argTypes[i] >> 16 & 0xFF;
				if(arraysize == 0) arraysize = 1;
				int type_size;
				switch(type){
					type_size = 0;
					case ARG_CHAR:
						type_size = sizeof(char) * arraysize;
					break;
					case ARG_SHORT:
						type_size = sizeof(short) * arraysize;
					break;
					case ARG_INT:
						type_size = sizeof(int) * arraysize;
					break;
					case ARG_LONG:
						type_size = sizeof(long) * arraysize;
					break;
					case ARG_DOUBLE:
						type_size = sizeof(double) * arraysize;
					break;
					case ARG_FLOAT:
						type_size = sizeof(float) * arraysize;
					break;
					default:
					break;
				}
				arg_size += type_size;
			}
			*buffersize = name_len + arg_num_len + arg_num * sizeof(int) + arg_size;
			buffer = new char[*buffersize];
			memset(&buffer[0], 0, *buffersize);
            cout << "buffersize" << *buffersize << endl;
            cout << "arg size: " << arg_size << endl;
			// buffer offset
			offset = 0;
			
			// function name
			strcpy(&buffer[offset], name);
			offset += name_len;

			//argnum
			memcpy(&buffer[offset], &arg_num, sizeof(int));
			offset += arg_num_len;

			cout << "argnum: " << arg_num << endl; 
			//argtypes
			index = 0;
			while(argTypes[index] != 0){
				memcpy(&buffer[offset], &argTypes[index], sizeof(int));
				offset += sizeof(int);
				index++;
			}
			memcpy(&buffer[offset], &argTypes[index], sizeof(int));
			offset += sizeof(int);
			cout << "copy arguments" << endl;
			cout << "offset : " << offset << endl;
			//args
			for(int i = 0; i < type_len; i++) {
				int arraysize = argTypes[i] & 0x0000FFFF;
				int type = argTypes[i] >> 16 & 0xFF;
				if(arraysize == 0) arraysize = 1;
				int type_size;
                cout << "type: " << type << " arraysize: " << arraysize << endl;
				switch(type){
					case ARG_CHAR:
						type_size = sizeof(char) * arraysize;
                        cout << "there" << endl;
						memcpy(&buffer[offset], (char *)args[i], type_size);
                        cout << "here" << endl;
						offset += type_size;
					break;
					case ARG_SHORT:
						type_size = sizeof(short) * arraysize;
						memcpy(&buffer[offset], (short *)args[i], type_size);
						offset += type_size;
					break;
					case ARG_INT:
						type_size = sizeof(int) * arraysize;
						memcpy(&buffer[offset], (int *)args[i], type_size);
						offset += type_size;
					break;
					case ARG_LONG:
						type_size = sizeof(long) * arraysize;
						memcpy(&buffer[offset], (long *)args[i], type_size);
						offset += type_size;
					break;
					case ARG_DOUBLE:
						type_size = sizeof(double) * arraysize;
						memcpy(&buffer[offset], (double *)args[i], type_size);
						offset += type_size;
					break;
					case ARG_FLOAT:
						type_size = sizeof(float) * arraysize;
						memcpy(&buffer[offset], (float *)args[i], type_size);
						offset += type_size;
					break;
					default:
						cerr << "error" << endl;
					break;
				}
			}
			return buffer;
		break;
		
	}
	return NULL;
}


void myUnmarshall(int type, char *buffer, int buffersize, char *name, int *argTypes, 
				void** args, in_addr_t *ip, int *port, int *reasoncode){
	int ip_len, port_len, name_len;
	int offset, index, argnum;
	switch(type){
		case 1: // LOC_REQUEST
			//name, argTypes
			name_len = 20;
			offset = 0;
			// name
			strncpy(name, &buffer[offset], name_len);
			offset += name_len;
			//argtypes
			index = 0;
			while(offset < buffersize){
				memcpy( &argTypes[index], &buffer[offset], sizeof(int));
				offset += sizeof(int);
				index++;
			}
			argTypes[index] = 0; // end of argType
			break;
		case 2: // LOC_SUCCESS
			ip_len = sizeof(in_addr_t);
			port_len = sizeof(int);
			offset = 0;
			memcpy(ip, &buffer[offset], sizeof(in_addr_t));
			offset += ip_len;
			memcpy(port, &buffer[offset], sizeof(int));
			offset += port_len;
			break;
			
		case 3:
		case 7: // LOC_FAILURE	//EXECUTE_FAILURE
			offset = 0;
			memcpy(reasoncode, &buffer[offset], sizeof(int));
			offset += sizeof(int);
			break;
		case 4:
			//REGISTER" IP address, port, name, argTypes
			ip_len = 4;
			port_len = 4;
			name_len = 20;
			
			offset = 0;
			memcpy(ip, &buffer[offset], sizeof(in_addr_t));
			offset += ip_len;
			memcpy(port, &buffer[offset], sizeof(int));
			offset += port_len;
			strncpy(name,&buffer[offset], name_len);
			offset += name_len;
			//argtypes			
			index = 0;
			while(offset < buffersize){
				memcpy(&argTypes[index], &buffer[offset], sizeof(int));
				offset += sizeof(int);
				index++;
			}
			argTypes[index] = 0; // end of argType
			break;
		case 5: //EXECUTE
			name_len = 20;
			offset = 0;
			memcpy(name, &buffer[offset], name_len);
			offset += name_len;
			memcpy(&argnum, &buffer[offset], sizeof(int));
			offset += sizeof(int);
			
			//argtypes			
			index = 0;
			while(index < argnum){
				memcpy(&argTypes[index], &buffer[offset], sizeof(int));
				offset += sizeof(int);
				index++;
			}
			// allocate args
			for(int i = 0; i < argnum-1; i++) {
				int arraysize = argTypes[i] & 0x0000FFFF;
				int type = argTypes[i] >> 16 & 0xFF;
				if(arraysize == 0) arraysize = 1;
				int type_size;
				switch(type){
					case ARG_CHAR:
						type_size = sizeof(char) * arraysize;
						args[i] = new char[type_size];
						memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
					break;
					case ARG_SHORT:
						type_size = sizeof(short) * arraysize;
						args[i] = new short[type_size];
						memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
					break;
					case ARG_INT:
						type_size = sizeof(int) * arraysize;
						args[i] = new int[type_size];
						memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
					break;
					case ARG_LONG:
						type_size = sizeof(long) * arraysize;
						args[i] = new long[type_size];
						memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
					break;
					case ARG_DOUBLE:
						type_size = sizeof(double) * arraysize;
						args[i] = new double[type_size];
						memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
					break;
					case ARG_FLOAT:
						type_size = sizeof(float) * arraysize;
						args[i] = new float[type_size];
						memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
					break;
					default:
						cerr << "Warning: not support this case" << endl;
					break;
				}
			}
			break;
		case 6:
            cout << "in unmarshall case 6" << endl;
			name_len = 20;
			offset = 0;
			memcpy(name, &buffer[offset], name_len);
			offset += name_len;
			int argnum;
			memcpy(&argnum, &buffer[offset], sizeof(int));
			offset += sizeof(int);
			//argtypes			
			index = 0;
			while(index < argnum){
				memcpy(&argTypes[index], &buffer[offset], sizeof(int));
				offset += sizeof(int);
				index++;
			}
			// allocate args
			for(int i = 0; i < argnum-1; i++) {
                int output = argTypes[i] >> ARG_OUTPUT & 0x00000000FFFFFFFF;
                cout << "output: " << output << endl;
                if(i==0 && output == 0) break;
				int arraysize = argTypes[i] & 0x0000FFFF;
				int type = argTypes[i] >> 16 & 0xFF;
				if(arraysize == 0) arraysize = 1;
				int type_size;
				switch(type){
					case ARG_CHAR:
                        cout << "incase" << endl;
						type_size = sizeof(char) * arraysize;
                        cout << "type_size " << type_size << endl;
                        memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
                        cout << (char *)args[i] << endl;
					break;
					case ARG_SHORT:
						type_size = sizeof(short) * arraysize;
						memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
					break;
					case ARG_INT:
						type_size = sizeof(int) * arraysize;
						memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
					break;
					case ARG_LONG:
						type_size = sizeof(long) * arraysize;
						memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
					break;
					case ARG_DOUBLE:
						type_size = sizeof(double) * arraysize;
						memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
					break;
					case ARG_FLOAT:
						type_size = sizeof(float) * arraysize;
						memcpy(args[i], &buffer[offset], type_size);
						offset += type_size;
					break;
					default:
						cerr << "Warning: not support this case" << endl;
					break;
				}
			}
			break;
	}
}
